﻿//Strategy.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Strategies
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Linq;

	using Ecng.Common;
	using Ecng.Collections;
	using Ecng.ComponentModel;
	using Ecng.Serialization;

	using StockSharp.Logging;
	using StockSharp.Algo.Commissions;
	using StockSharp.Algo.Latency;
	using StockSharp.Algo.PnL;
	using StockSharp.Algo.Positions;
	using StockSharp.Algo.Slippage;
	using StockSharp.Algo.Statistics;
	using StockSharp.BusinessEntities;

	using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;

	/// <summary>
	/// Базовый класс для всех торговых стратегий.
	/// </summary>
	public class Strategy : BaseLogReceiver<Strategy>, INotifyPropertyChangedEx, IPersistable, IMarketRuleContainer, ICloneable<Strategy>
	{
		sealed class ChildStrategyList : SynchronizedSet<Strategy>, IStrategyChildStrategyList
		{
			private readonly SynchronizedDictionary<Strategy, IMarketRule> _childStrategyRules = new SynchronizedDictionary<Strategy, IMarketRule>();
			private readonly Strategy _parent;

			public ChildStrategyList(Strategy parent)
				: base(true)
			{
				if (parent == null)
					throw new ArgumentNullException("parent");

				_parent = parent;
			}
			
			protected override void OnAdded(Strategy item)
			{
				//pyh: Нельзя использовать OnAdding тк логирование включается по событию Added которое вызовет base.OnAdded
				base.OnAdded(item);

				if (item.Parent != null)
					throw new ArgumentException("Стратегия уже добавлена в качестве дочерней.");

				item.Parent = _parent;

				if (item.Trader == null)
					item.Trader = _parent.Trader;

				if (item.Portfolio == null)
					item.Portfolio = _parent.Portfolio;

				if (item.Security == null)
					item.Security = _parent.Security;

				if (!_parent.OnlyLogEvent)
				{
					item.OrderRegistering += _parent.OnOrderRegistering;
					item.OrderRegistered += _parent.AddOrder;
					item.ReRegistering += _parent.ReRegisterSlippage;
					item.OrderChanged += _parent.OnChildOrderChanged;
					item.OrderRegisterFailed += _parent.OnChildOrderRegisterFailed;
					item.OrderCancelFailed += _parent.OnChildOrderCancelFailed;
					item.StopOrderRegistering += _parent.OnStopOrderRegistering;
					item.StopOrderRegistered += _parent.AddOrder;
					item.StopOrderChanged += _parent.OnChildStopOrderChanged;
					item.StopOrderRegisterFailed += _parent.OnChildStopOrderRegisterFailed;
					item.StopOrderCancelFailed += _parent.OnChildStopOrderCancelFailed;
					item.NewMyTrades += _parent.AddMyTrades;
					item.OrderReRegistering += _parent.OnOrderReRegistering;
					item.StopOrderReRegistering += _parent.OnStopOrderReRegistering;
				}

				item.ProcessStateChanged += OnChildProcessStateChanged;

				item.StopOrders.ForEach(_parent.AddOrder);
				item.Orders.ForEach(_parent.AddOrder);

				if (!item.MyTrades.IsEmpty())
					_parent.AddMyTrades(item.MyTrades);

				_parent._orderFails.AddRange(item.OrderFails);

				if (item.ProcessState == _parent.ProcessState && _parent.ProcessState == ProcessStates.Started)
					OnChildProcessStateChanged(item);
				else
					item.ProcessState = _parent.ProcessState;
			}

			private void OnChildProcessStateChanged(Strategy child)
			{
				if (child.ProcessState == ProcessStates.Started)
				{
					// для предотвращения остановки родительской стратегии пока работают ее дочерние
					var rule =
						child
							.WhenStopped()
							.Do(() => _childStrategyRules.Remove(child))
							.Once()
							.Apply(_parent);

					rule.UpdateName(rule.Name + " (ChildStrategyList.OnChildProcessStateChanged)");

					_childStrategyRules.Add(child, rule);
				}
			}

			protected override bool OnClearing()
			{
				foreach (var item in ToArray())
					Remove(item);

				return true;
			}

			protected override bool OnRemoving(Strategy item)
			{
				item.Parent = null;

				if (!_parent.OnlyLogEvent)
				{
					item.OrderRegistering -= _parent.OnOrderRegistering;
					item.OrderRegistered -= _parent.AddOrder;
					item.ReRegistering -= _parent.ReRegisterSlippage;
					item.OrderChanged -= _parent.OnChildOrderChanged;
					item.OrderRegisterFailed -= _parent.OnChildOrderRegisterFailed;
					item.OrderCancelFailed -= _parent.OnChildOrderCancelFailed;
					item.OrderCanceling -= _parent.OnOrderCanceling;
					item.StopOrderRegistering -= _parent.OnStopOrderRegistering;
					item.StopOrderRegistered -= _parent.AddOrder;
					item.StopOrderChanged -= _parent.OnChildStopOrderChanged;
					item.StopOrderRegisterFailed -= _parent.OnChildStopOrderRegisterFailed;
					item.StopOrderCancelFailed -= _parent.OnChildStopOrderCancelFailed;
					item.StopOrderCanceling -= _parent.OnStopOrderCanceling;
					item.NewMyTrades -= _parent.AddMyTrades;
					item.OrderReRegistering -= _parent.OnOrderReRegistering;
					item.StopOrderReRegistering -= _parent.OnStopOrderReRegistering;
				}
				
				item.ProcessStateChanged -= OnChildProcessStateChanged;

				_childStrategyRules.SyncDo(d =>
				{
					var rule = d.TryGetValue(item);

					if (rule == null)
						return;

					// правило могло быть удалено при остановке дочерней стратегии, но перед ее удалением из коллекции у родителя
					if (rule.IsReady)
						_parent.TryRemoveRule(rule);

					d.Remove(item);
				});

				return base.OnRemoving(item);
			}

			public void TryRemoveStoppedRule(IMarketRule rule)
			{
				var child = rule.Token as Strategy;

				if (child != null)
					_childStrategyRules.Remove(child);
			}
		}

		private sealed class StrategyRuleList : MarketRuleList
		{
			private readonly Strategy _strategy;

			public StrategyRuleList(Strategy strategy)
				: base(strategy)
			{
				if (strategy == null)
					throw new ArgumentNullException("strategy");

				_strategy = strategy;
			}

			protected override bool OnAdding(IMarketRule item)
			{
				return _strategy.ProcessState != ProcessStates.Stopping && base.OnAdding(item);
			}
		}

		// mika
		// причина создания своего RW (вместо ReaderWriterLockSlim)
		// у нас Read может повлечь за собой Write (Strategy.Dispose)
		// но Upgrade возможен только для одного потока (а таких потоков в Strategy может быть много)
		// как итог, TryInvoke для нормальных событий (OnNewOrders например) возвращал false хотя никакого Dispose не было вызвано

		// mika обновление от 29.09.2011
		// если из Read вызвать Write, то все виснет (в случае стратегий распространенное явление, когда Dispose происходит по какому по какому-то событию)
		// поэтому сделал Write асинхронным
		private sealed class SimpleReadWriteAsyncLock
		{
			private int _readingCount;

			private readonly object _writeLock = new object();
			private readonly List<Action> _pendingWrites = new List<Action>();

			public void Read(Action action)
			{
				if (action == null)
					throw new ArgumentNullException("action");

				lock (_writeLock)
				{
					_readingCount++;
				}

				try
				{
					action();
				}
				finally
				{
					lock (_writeLock)
					{
						_readingCount--;
						//Monitor.PulseAll(_writeLock);

						if (_readingCount == 0)
						{
							foreach (var write in _pendingWrites)
								write();

							_pendingWrites.Clear();
						}
					}
				}
			}

			public void WriteAsync(Action action)
			{
				if (action == null)
					throw new ArgumentNullException("action");

				lock (_writeLock)
				{
					if (_readingCount == 0)
						action();
					else
						_pendingWrites.Add(action);
				}
			}
		}

		private sealed class OrderInfo
		{
			public bool IsOwn { get; set; }
			public bool IsCanceled { get; set; }
			public bool IsHandled { get; set; }
			public decimal ReceivedVolume { get; set; }
		}

		private readonly CachedSynchronizedSet<Order> _pendingOrders = new CachedSynchronizedSet<Order>();
		private readonly SynchronizedSet<Order> _pendingStopOrders = new SynchronizedSet<Order>();

		private readonly SynchronizedDictionary<Order, OrderInfo> _ordersInfo = new SynchronizedDictionary<Order, OrderInfo>();

		private readonly StrategyNameGenerator _nameGenerator;

		private readonly SimpleReadWriteAsyncLock _disposeLock = new SimpleReadWriteAsyncLock();

		private DateTime _firstOrderTime;
		private DateTime _lastOrderTime;
		private TimeSpan _maxOrdersKeepTime;

		private const string _category = "Общие";

		/// <summary>
		/// Создать <see cref="Strategy"/>.
		/// </summary>
		public Strategy()
		{
			_childStrategies = new ChildStrategyList(this);
			SetChilds(ChildStrategies);

			Rules = new StrategyRuleList(this);

			_nameGenerator = new StrategyNameGenerator(this);
			_nameGenerator.Changed += name => _name.Value = name;

			UnrealizedPnLInterval = TimeSpan.FromMinutes(1);

			_id = this.Param("Id", base.Id);
			_volume = this.Param<decimal>("Volume", 1);
			_name = this.Param("Name", new string(GetType().Name.Where(char.IsUpper).ToArray()));
			_maxErrorCount = this.Param("MaxErrorCount", 1);
			_disposeOnStop = this.Param("DisposeOnStop", false);
			_cancelOrdersWhenStopping = this.Param("CancelOrdersWhenStopping", true);
			_waitAllTrades = this.Param<bool>("WaitAllTrades");
			_commentOrders = this.Param<bool>("CommentOrders");
			_ordersKeepTime = this.Param("OrdersKeepTime", TimeSpan.FromDays(1));
			_logLevel = this.Param("LogLevel", LogLevels.Inherit);

			InitMaxOrdersKeepTime();

			MemoryStatistics.Instance.Strategies.Add(this);
		}

		/// <summary>
		/// Подписаться только на Log события.
		/// </summary>
		/// <remarks>
		/// Данный флаг необходим для StrategyMonitorWindow.
		/// </remarks>
		public bool OnlyLogEvent { private get; set; }

		private readonly StrategyParam<Guid> _id;

		/// <summary>
		/// Уникальный идентификатор источника.
		/// </summary>
		public override Guid Id
		{
			get { return _id.Value; }
			set
			{
				if (value == Id)
					return;

				_id.Value = value;
			}
		}

		private readonly StrategyParam<LogLevels> _logLevel;

		/// <summary>
		/// Уровень логирования. По-умолчанию установлено в <see cref="LogLevels.Inherit"/>.
		/// </summary>
		[Category("Логирование")]
		//[PropertyOrder(8)]
		[DisplayName("Уровень логирования")]
		[Description("Уровень логирования стратегии.")]
		public override LogLevels LogLevel
		{
			get { return _logLevel.Value; }
			set { _logLevel.Value = value; }
		}

		private readonly StrategyParam<string> _name;

		/// <summary>
		/// Название стратегии.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(0)]
		[DisplayName("Название")]
		[Description("Название стратегии.")]
		public override string Name
		{
			get { return _name.Value; }
			set
			{
				if (value == Name)
					return;

				_nameGenerator.Value = value;
				_name.Value = value;
			}
		}

		/// <summary>
		/// Генератор имени стратегии.
		/// </summary>
		[Browsable(false)]
		public StrategyNameGenerator NameGenerator { get { return _nameGenerator; } }

		private ITrader _trader;

		/// <summary>
		/// Шлюз к торговой системе.
		/// </summary>
		[Browsable(false)]
		public virtual ITrader Trader
		{
			get { return _trader; }
			set
			{
				if (Trader == value)
					return;

				if (_trader != null)
				{
					_trader.NewOrders -= OnTraderNewOrders;
					_trader.OrdersChanged -= OnTraderOrdersChanged;
					_trader.OrdersRegisterFailed -= OnTraderOrdersRegisterFailed;
					_trader.OrdersCancelFailed -= OnTraderOrdersCancelFailed;
					_trader.NewStopOrders -= OnTraderNewStopOrders;
					_trader.StopOrdersChanged -= OnTraderStopOrdersChanged;
					_trader.StopOrdersRegisterFailed -= OnTraderStopOrdersRegisterFailed;
					_trader.StopOrdersCancelFailed -= OnTraderStopOrdersCancelFailed;
					_trader.NewMyTrades -= OnTraderNewMyTrades;
					_trader.SecuritiesChanged -= OnTraderSecuritiesChanged;
					_trader.PortfoliosChanged -= OnTraderPortfoliosChanged;
					_trader.PositionsChanged -= OnTraderPositionsChanged;
				}

				_trader = value;

				if (_trader != null && !OnlyLogEvent)
				{
					_trader.NewOrders += OnTraderNewOrders;
					_trader.OrdersChanged += OnTraderOrdersChanged;
					_trader.OrdersRegisterFailed += OnTraderOrdersRegisterFailed;
					_trader.OrdersCancelFailed += OnTraderOrdersCancelFailed;
					_trader.NewStopOrders += OnTraderNewStopOrders;
					_trader.StopOrdersChanged += OnTraderStopOrdersChanged;
					_trader.StopOrdersRegisterFailed += OnTraderStopOrdersRegisterFailed;
					_trader.StopOrdersCancelFailed += OnTraderStopOrdersCancelFailed;
					_trader.NewMyTrades += OnTraderNewMyTrades;
					_trader.SecuritiesChanged += OnTraderSecuritiesChanged;
					_trader.PortfoliosChanged += OnTraderPortfoliosChanged;
					_trader.PositionsChanged += OnTraderPositionsChanged;
				}

				ChildStrategies.SyncDo(c =>
				{
					foreach (var strategy in c)
					{
						if (strategy.Trader == null || value == null)
							strategy.Trader = value;
					}
				});

				TraderChanged.SafeInvoke();
			}
		}

		private Portfolio _portfolio;

		/// <summary>
		/// Портфель.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(1)]
		[DisplayName("Портфель")]
		[Description("Торговый портфель, через который будут совершаться операции с заявками.")]
		public virtual Portfolio Portfolio
		{
			get { return _portfolio; }
			set
			{
				if (_portfolio == value)
					return;

				_portfolio = value;

				ChildStrategies.SyncDo(c =>
				{
					foreach (var strategy in c)
					{
						if (strategy.Portfolio == null)
							strategy.Portfolio = value;
					}
				});

				RaiseParametersChanged("Portfolio");
			}
		}

		private Security _security;

		/// <summary>
		/// Инструмент.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(2)]
		[DisplayName("Инструмент")]
		[Description("Торговый инструмент, с которым работает стратегия.")]
		public virtual Security Security
		{
			get { return _security; }
			set
			{
				if (_security == value)
					return;

				_security = value;

				ChildStrategies.SyncDo(c =>
				{
					foreach (var strategy in c)
					{
						if (strategy.Security == null)
							strategy.Security = value;
					}
				});
				
				//pyh: валится в BasketTrader.GetMarketTime(Exchange.RTS) в Студии
				//OnSecurityChanged();
				RaiseParametersChanged("Security");
			}
		}

		private ISlippageManager _slippageManager = new SlippageManager();

		/// <summary>
		/// Менеджер проскальзывания. Учитывает сделки данной стратегии, а так же ее дочерних стратегий <see cref="ChildStrategies"/>.
		/// </summary>
		[Browsable(false)]
		public ISlippageManager SlippageManager
		{
			get { return _slippageManager; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_slippageManager = value;
			}
		}

		/// <summary>
		/// Суммарное значение проскальзывания.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(4)]
		[DisplayName("Проскальзывание")]
		[Description("Суммарное значение проскальзывания.")]
		[ReadOnly(true)]
		public decimal Slippage
		{
			get { return SlippageManager.Slippage; }
		}

		/// <summary>
		/// Событие изменения <see cref="Slippage"/>.
		/// </summary>
		public event Action SlippageChanged;

		private IPnLManager _pnLManager = new PnLManager();

		/// <summary>
		/// Менеджер прибыли-убытка. Учитывает сделки данной стратегии, а так же ее дочерних стратегий <see cref="ChildStrategies"/>.
		/// </summary>
		[Browsable(false)]
		public IPnLManager PnLManager
		{
			get { return _pnLManager; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_pnLManager = value;
			}
		}

		/// <summary>
		/// Суммарное значение прибыли-убытка без учета комиссии <see cref="Commission"/>.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(5)]
		[DisplayName("P&L")]
		[Description("Суммарное значение прибыли-убытка.")]
		[ReadOnly(true)]
		public decimal PnL
		{
			get { return PnLManager.PnL; }
		}

		/// <summary>
		/// Событие изменения <see cref="PnL"/>.
		/// </summary>
		public event Action PnLChanged;

		private ICommissionManager _commissionManager = new CommissionManager();

		/// <summary>
		/// Менеджер комиссии.
		/// </summary>
		[Browsable(false)]
		public ICommissionManager CommissionManager
		{
			get { return _commissionManager; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_commissionManager = value;
			}
		}

		/// <summary>
		/// Суммарное значение комиссии.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(6)]
		[DisplayName("Комиссия")]
		[Description("Суммарное значение комиссии.")]
		[ReadOnly(true)]
		public decimal Commission
		{
			get { return CommissionManager.Commission; }
		}

		/// <summary>
		/// Событие изменения <see cref="Commission"/>.
		/// </summary>
		public event Action CommissionChanged;

		private IPositionManager _positionManager = new PositionManager(true);

		/// <summary>
		/// Менеджер позиции. Учитывает сделки данной стратегии, а так же ее дочерних стратегий <see cref="ChildStrategies"/>.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(7)]
		[DisplayName("Позиция")]
		[Description("Позиция стратегии по инструментам и портфелям.")]
		public IPositionManager PositionManager
		{
			get { return _positionManager; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_positionManager = value;
			}
		}

		/// <summary>
		/// Суммарное значение позиции.
		/// </summary>
		[Browsable(false)]
		public decimal Position
		{
			get { return PositionManager.Position; }
			set
			{
				if (Position == value)
					return;

				PositionManager.Position = value;
				RaisePositionChanged();
			}
		}

		/// <summary>
		/// Событие изменения <see cref="Position"/>.
		/// </summary>
		public event Action PositionChanged;

		private ILatencyManager _latencyManager = new LatencyManager();

		/// <summary>
		/// Менеджер задержки. Учитывает заявки данной стратегии, а так же ее дочерних стратегий <see cref="ChildStrategies"/>.
		/// </summary>
		[Browsable(false)]
		public ILatencyManager LatencyManager
		{
			get { return _latencyManager; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_latencyManager = value;
			}
		}

		/// <summary>
		/// Суммарное значение задержки.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(8)]
		[DisplayName("Задержка")]
		[Description("Суммарное значение задержки.")]
		[ReadOnly(true)]
		public TimeSpan Latency
		{
			get { return LatencyManager.LatencyRegistration + LatencyManager.LatencyCancellation; }
		}

		/// <summary>
		/// Событие изменения <see cref="Latency"/>.
		/// </summary>
		public event Action LatencyChanged;

		private StatisticManager _statisticManager = new StatisticManager();

		/// <summary>
		/// Менеджер статистики.
		/// </summary>
		[Browsable(false)]
		public virtual StatisticManager StatisticManager
		{
			get { return _statisticManager; }
			protected set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				_statisticManager = value;
			}
		}

		private readonly SynchronizedSet<IStrategyParam> _parameters = new SynchronizedSet<IStrategyParam>();

		/// <summary>
		/// Параметры стратегии.
		/// </summary>
		[Browsable(false)]
		public virtual ISynchronizedCollection<IStrategyParam> Parameters
		{
			get { return _parameters; }
		}

		/// <summary>
		/// Событие изменения <see cref="Parameters"/>.
		/// </summary>
		public event Action ParametersChanged;

		/// <summary>
		/// Вызвать события <see cref="ParametersChanged"/> и <see cref="PropertyChanged"/>.
		/// </summary>
		/// <param name="name">Название параметра.</param>
		protected internal void RaiseParametersChanged(string name)
		{
			ParametersChanged.SafeInvoke();
			this.Notify(name);
		}

		private readonly SettingsStorage _environment = new SettingsStorage();

		/// <summary>
		/// Параметры окружения стратегии. 
		/// </summary>
		[Browsable(false)]
		public virtual SettingsStorage Environment
		{
			get { return _environment; }
		}

		private readonly StrategyParam<int> _maxErrorCount;

		/// <summary>
		/// Максимальное количество ошибок, которое должна получить стратегия прежде, чем она остановил работу.
		/// </summary>
		/// <remarks>Значение по умолчанию равно 1.</remarks>
		[Browsable(false)]
		public int MaxErrorCount
		{
			get { return _maxErrorCount.Value; }
			set
			{
				if (value < 1)
					throw new ArgumentOutOfRangeException("value", value, "Максимальное количество ошибок не может быть меньше одного.");

				_maxErrorCount.Value = value;
			}
		}

		private int _errorCount;

		/// <summary>
		/// Текущее количество ошибок.
		/// </summary>
		[Browsable(false)]
		public int ErrorCount
		{
			get { return _errorCount; }
			private set
			{
				if (_errorCount == value)
					return;

				_errorCount = value;
				this.Notify("ErrorCount");
			}
		}

		private ProcessStates _processState;

		/// <summary>
		/// Состояние работы.
		/// </summary>
		[Browsable(false)]
		public ProcessStates ProcessState
		{
			get { return _processState; }
			private set
			{
				if (_processState == value)
					return;

				this.AddDebugLog("Переход из состояния {0} в {1}.", _processState, value);

				if (_processState == ProcessStates.Stopped && value == ProcessStates.Stopping)
					throw new InvalidOperationException("Стратегия {0} уже остановлена, и не может быть переведена в состояние {1}.".Put(Name, value));

				_processState = value;

				ChildStrategies.SyncDo(c =>
				{
					var child = (IEnumerable<Strategy>)c;

					if (ProcessState == ProcessStates.Stopping)
						child = child.Where(s => s.ProcessState == ProcessStates.Started);

					child.ToArray().ForEach(s => s.ProcessState = ProcessState);
				});

				try
				{
					switch (value)
					{
						case ProcessStates.Started:
						{
							StartedTime = this.GetMarketTime();
							LogProcessState(value);
							OnStarted();
							break;
						}
						case ProcessStates.Stopping:
						{
							LogProcessState(value);
							OnStopping();
							break;
						}
						case ProcessStates.Stopped:
						{
							TotalWorkingTime += this.GetMarketTime() - StartedTime;
							StartedTime = default(DateTime);
							LogProcessState(value);
							OnStopped();
							break;
						}
					}

					ProcessStateChanged.SafeInvoke(this);
					this.Notify("ProcessState");
				}
				catch (Exception error)
				{
					OnError(error);
				}
				
				if (ProcessState == ProcessStates.Stopping)
				{
					if (CancelOrdersWhenStopping)
					{
						this.AddInfoLog("Ожидание снятия всех активных заявок.");
						ProcessCancelActiveOrders();
					}

					foreach (var rule in Rules.SyncGet(c => c.ToArray()))
					{
						if (this.TryRemoveRule(rule))
							_childStrategies.TryRemoveStoppedRule(rule);
					}

					TryFinalStop();
				}
			}
		}

		private void LogProcessState(ProcessStates state)
		{
			string stateStr;

			switch (state)
			{
				case ProcessStates.Stopped:
					stateStr = "остановлена";
					break;
				case ProcessStates.Stopping:
					stateStr = "останавливается";
					break;
				case ProcessStates.Started:
					stateStr = "запущена";
					break;
				default:
					throw new ArgumentOutOfRangeException("state");
			}

			this.AddInfoLog("Стратегия {0}. [{1},{2}]. Позиция при старте {3}.", stateStr, ChildStrategies.Count, Parent != null ? Parent.ChildStrategies.Count : -1, Position);
		}

		/// <summary>
		/// Событие изменения <see cref="ProcessState"/>.
		/// </summary>
		public event Action<Strategy> ProcessStateChanged;

		private readonly StrategyParam<bool> _cancelOrdersWhenStopping;

		/// <summary>
		/// Снимать активные заявки при остановке. По-умолчанию включено.
		/// </summary>
		[Browsable(false)]
		public bool CancelOrdersWhenStopping
		{
			get { return _cancelOrdersWhenStopping.Value; }
			set { _cancelOrdersWhenStopping.Value = value; }
		}

		private readonly CachedSynchronizedSet<Order> _orders = new CachedSynchronizedSet<Order> { ThrowIfDuplicate = true };
		//private readonly CachedSynchronizedList<Order> _orders = new CachedSynchronizedList<Order>();

		/// <summary>
		/// Заявки, зарегистрированные в рамках стратегии.
		/// </summary>
		[Browsable(false)]
		public IEnumerable<Order> Orders
		{
			get { return _orders.Cache; }
		}

		private readonly CachedSynchronizedSet<Order> _stopOrders = new CachedSynchronizedSet<Order> { ThrowIfDuplicate = true };
		//private readonly CachedSynchronizedList<Order> _stopOrders = new CachedSynchronizedList<Order>();

		/// <summary>
		/// Стоп-заявки, зарегистрированные в рамках стратегии.
		/// </summary>
		[Browsable(false)]
		public IEnumerable<Order> StopOrders
		{
			get { return _stopOrders.Cache; }
		}

		private readonly StrategyParam<TimeSpan> _ordersKeepTime;

		/// <summary>
		/// Время хранения заявок <see cref="Orders"/> и <see cref="StopOrders"/> в памяти.
		/// По-умолчанию равно 2-ум дням. Если значение установлено в <see cref="TimeSpan.Zero"/>, то заявки не будут удаляться.
		/// </summary>
		[Browsable(false)]
		public TimeSpan OrdersKeepTime
		{
			get { return _ordersKeepTime.Value; }
			set
			{
				if (value < TimeSpan.Zero)
					throw new ArgumentOutOfRangeException("value", value, "Время хранения заявок не может быть отрицательным.");

				_ordersKeepTime.Value = value;
				InitMaxOrdersKeepTime();
				RecycleOrders();
			}
		}

		private void InitMaxOrdersKeepTime()
		{
			_maxOrdersKeepTime = TimeSpan.FromTicks((long)(OrdersKeepTime.Ticks * 1.5));
		}

		private readonly CachedSynchronizedSet<MyTrade> _myTrades = new CachedSynchronizedSet<MyTrade> { ThrowIfDuplicate = true };

		/// <summary>
		/// Cделки, прошедшие в течении работы стратегии.
		/// </summary>
		[Browsable(false)]
		public IEnumerable<MyTrade> MyTrades
		{
			get { return _myTrades.Cache; }
		}

		private readonly CachedSynchronizedSet<OrderFail> _orderFails = new CachedSynchronizedSet<OrderFail> { ThrowIfDuplicate = true };

		/// <summary>
		/// Заявки с ошибками, зарегистрированные в рамках стратегии.
		/// </summary>
		[Browsable(false)]
		public IEnumerable<OrderFail> OrderFails
		{
			get { return _orderFails.Cache; }
		}

		private readonly StrategyParam<decimal> _volume;

		/// <summary>
		/// Объем, которым необходимо оперировать.
		/// </summary>
		/// <remarks>
		/// Если значение установлено в 0, то параметр игнорируется.
		/// </remarks>
		[Category(_category)]
		[PropertyOrder(3)]
		[DisplayName("Объем")]
		[Description("Объем, которым необходимо оперировать.")]
		public decimal Volume
		{
			get { return _volume.Value; }
			set
			{
				if (value < 0)
					throw new ArgumentOutOfRangeException("value", value, "Объем стратегии не может быть меньше нуля.");

				_volume.Value = value;
			}
		}

		private LogLevels _errorState;

		/// <summary>
		/// Состояние ошибки.
		/// </summary>
		[Browsable(false)]
		public LogLevels ErrorState
		{
			get { return _errorState; }
			private set
			{
				if (_errorState == value)
					return;

				_errorState = value;
				this.Notify("ErrorState");
			}
		}

		private readonly ChildStrategyList _childStrategies;

		/// <summary>
		/// Дочерние торговые стратегии.
		/// </summary>
		[Browsable(false)]
		public IStrategyChildStrategyList ChildStrategies
		{
			get { return _childStrategies; }
		}

		private DateTime _startedTime;

		/// <summary>
		/// Время запуска стратегии.
		/// </summary>
		[Category(_category)]
		[PropertyOrder(3)]
		[DisplayName("Время запуска")]
		[Description("Время запуска стратегии.")]
		[ReadOnly(true)]
		public DateTime StartedTime
		{
			get { return _startedTime; }
			private set
			{
				_startedTime = value;
				this.Notify("StartedTime");
			}
		}

		private TimeSpan _totalWorkingTime;

		/// <summary>
		/// Общее время работы стратегии с вычетом временных отрезков, когда стратегия останавливалась.
		/// </summary>
		[Browsable(false)]
		public TimeSpan TotalWorkingTime
		{
			get
			{
				var retVal = _totalWorkingTime;

				if (StartedTime != default(DateTime) && Trader != null)
					retVal += this.GetMarketTime() - StartedTime;

				return retVal;
			}
			private set
			{
				if (_totalWorkingTime == value)
					return;

				_totalWorkingTime = value;
				this.Notify("TotalWorkingTime");
			}
		}

		private readonly StrategyParam<bool> _disposeOnStop;

		/// <summary>
		/// Автоматически освобождать занятые ресурсы стратерии при ее остановке
		/// (состояние <see cref="ProcessState"/> стало равным <see cref="ProcessStates.Stopped"/>)
		/// и удалять ее из родительской через <see cref="ChildStrategies"/>.
		/// </summary>
		/// <remarks>Режим используется только для одноразовых стратегий, тоесть для тех, что не будут запущены повторно (например, котирование).</remarks>
		/// <remarks>По умолчанию выключено.</remarks>
		[Browsable(false)]
		public bool DisposeOnStop
		{
			get { return _disposeOnStop.Value; }
			set { _disposeOnStop.Value = value; }
		}

		private readonly StrategyParam<bool> _waitAllTrades;

		/// <summary>
		/// Останавливать стратегию только после получения всех сделок по зарегистрированным заявкам.
		/// </summary>
		/// <remarks>По умолчанию выключено.</remarks>
		[Browsable(false)]
		public bool WaitAllTrades
		{
			get { return _waitAllTrades.Value; }
			set { _waitAllTrades.Value = value; }
		}

		private readonly StrategyParam<bool> _commentOrders;

		/// <summary>
		/// Добавлять в <see cref="Order.Comment"/> название стратегии <see cref="Name"/>, выставившая заявку.
		/// </summary>
		/// <remarks>По умолчанию выключено.</remarks>
		[Browsable(false)]
		public bool CommentOrders
		{
			get { return _commentOrders.Value; }
			set { _commentOrders.Value = value; }
		}

		/// <summary>
		/// Зарегистрированные правила.
		/// </summary>
		[Browsable(false)]
		public IMarketRuleList Rules { get; private set; }

		private readonly object _rulesSuspendLock = new object();
		private int _rulesSuspendCount;

		/// <summary>
		/// Приостановлено ли исполнение правил.
		/// </summary>
		/// <remarks>
		/// Приостановка правил происходит через метод <see cref="SuspendRules()"/>.
		/// </remarks>
		[Browsable(false)]
		public bool IsRulesSuspended
		{
			get { return _rulesSuspendCount > 0; }
		}

		/// <summary>
		/// Событие отправки заявки на регистрацию.
		/// </summary>
		public event Action<Order> OrderRegistering;

		/// <summary>
		/// Событие об успешной регистрации заявки.
		/// </summary>
		public event Action<Order> OrderRegistered;

		/// <summary>
		/// Событие об ошибке регистрации заявки.
		/// </summary>
		public event Action<OrderFail> OrderRegisterFailed;

		/// <summary>
		/// Событие отправки стоп-заявки на регистрацию.
		/// </summary>
		public event Action<Order> StopOrderRegistering;

		/// <summary>
		/// Событие об успешной регистрации стоп-заявки.
		/// </summary>
		public event Action<Order> StopOrderRegistered;

		/// <summary>
		/// Событие об ошибке регистрации стоп-заявки.
		/// </summary>
		public event Action<OrderFail> StopOrderRegisterFailed;

		/// <summary>
		/// Событие об изменении заявки.
		/// </summary>
		public event Action<Order> OrderChanged;

		/// <summary>
		/// Событие об изменении стоп-заявки.
		/// </summary>
		public event Action<Order> StopOrderChanged;

		/// <summary>
		/// Событие отправки заявки на отмену.
		/// </summary>
		public event Action<Order> OrderCanceling;

		/// <summary>
		/// Событие отправки стоп-заявки на отмену.
		/// </summary>
		public event Action<Order> StopOrderCanceling;

		/// <summary>
		/// Событие отправки заявки на перерегистрацию.
		/// </summary>
		public event Action<Order, Order> OrderReRegistering;

		/// <summary>
		/// Событие отправки стоп-заявки на перерегистрацию.
		/// </summary>
		public event Action<Order, Order> StopOrderReRegistering;

		/// <summary>
		/// Событие об ошибке отмены заявки.
		/// </summary>
		public event Action<OrderFail> OrderCancelFailed;

		/// <summary>
		/// Событие об ошибке отмены стоп-заявки.
		/// </summary>
		public event Action<OrderFail> StopOrderCancelFailed;

		/// <summary>
		/// Событие о появлении новых сделок.
		/// </summary>
		public event Action<IEnumerable<MyTrade>> NewMyTrades;

		/// <summary>
		/// Событие изменения шлюза стратегии.
		/// </summary>
		public event Action TraderChanged;

		/// <summary>
		/// Событие изменения инструмента стратегии.
		/// </summary>
		public event Action SecurityChanged;

		/// <summary>
		/// Событие изменения портфеля стратегии.
		/// </summary>
		public event Action PortfolioChanged;

		/// <summary>
		/// Событие изменения позиций стратегии.
		/// </summary>
		public event Action<IEnumerable<Position>> PositionsChanged;

		/// <summary>
		/// Событие возникновения ошибки в стратегии.
		/// </summary>
		public event Action<Exception> Error;

		/// <summary>
		/// Метод вызывается тогда, когда вызвался метод <see cref="Start"/>, и состояние <see cref="ProcessState"/> перешло в значение <see cref="ProcessStates.Started"/>.
		/// </summary>
		protected virtual void OnStarted()
		{
			ThrowIfTraderNotRegistered();

			if (Security == null)
				throw new InvalidOperationException("Инструмент не инициализирован.");

			if (Portfolio == null)
				throw new InvalidOperationException("Портфель не инициализирован.");

			foreach (var parameter in Parameters)
			{
				var unit = parameter.Value as Unit;

				if (unit != null && unit.Security == null && (unit.Type == UnitTypes.Point || unit.Type == UnitTypes.Step))
					unit.Security = Security;
			}

			//pyh: Это сильно замедляет тестирование + объясните зачем это нужно.

			//ProcessNewOrders(Trader.StopOrders, true).ForEach(AddOrder);
			//ProcessNewOrders(Trader.Orders, false).ForEach(AddOrder);

			ErrorCount = 0;
			ErrorState = LogLevels.Info;
		}

		/// <summary>
		/// Метод вызывается тогда, когда состояние процесса <see cref="ProcessState"/> перешло в значение <see cref="ProcessStates.Stopping"/>.
		/// </summary>
		protected virtual void OnStopping()
		{
		}

		/// <summary>
		/// Метод вызывается тогда, когда состояние процесса <see cref="ProcessState"/> перешло в значение <see cref="ProcessStates.Stopped"/>.
		/// </summary>
		protected virtual void OnStopped()
		{
		}

		/// <summary>
		/// Зарегистрировать заявку и автоматически добавить для запуска механизмов расчета прибыли-убытка и проскальзывания.
		/// </summary>
		/// <param name="order">Заявка.</param>
		public virtual void RegisterOrder(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			this.AddInfoLog("Регистрация новой {0} (0x{5:X}) заявки на {1} с ценой {2} и объемом {3}. {4}",
				order.Type, order.Direction, order.Price, order.Volume, order.Comment, order.GetHashCode());

			ThrowIfTraderNotRegistered();

			if (ProcessState != ProcessStates.Started)
			{
				this.AddWarningLog("Стратегия в состоянии {0}. Регистрация заявки невозможна.", ProcessState);
				return;
			}

			if (order.Type == OrderTypes.Conditional)
				_pendingStopOrders.Add(order);
			else
				_pendingOrders.Add(order);

			_ordersInfo.Add(order, new OrderInfo { IsOwn = true });

			if (order.Security == null)
				order.Security = Security;

			if (order.Portfolio == null)
				order.Portfolio = Portfolio;

			//if (order.Strategy == null)
			//	order.Strategy = this;

			ApplyMonitorRules(order);

			if (order.Type == OrderTypes.Conditional)
				OnStopOrderRegistering(order);
			else
				OnOrderRegistering(order);

			Trader.RegisterOrder(order);
		}

		/// <summary>
		/// Перерегистрировать заявку и автоматически добавить для запуска механизмов расчета прибыли-убытка и проскальзывания.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять и на основе нее зарегистрировать новую.</param>
		/// <param name="newOrder">Новая заявка.</param>
		public virtual void ReRegisterOrder(Order oldOrder, Order newOrder)
		{
			if (oldOrder == null)
				throw new ArgumentNullException("oldOrder");

			if (newOrder == null)
				throw new ArgumentNullException("newOrder");

			this.AddInfoLog("Перерегистрация заявки {0} с ценой {1} на цену {2}. {3}", oldOrder.TransactionId, oldOrder.Price, newOrder.Price, oldOrder.Comment);

			ThrowIfTraderNotRegistered();

			if (ProcessState != ProcessStates.Started)
			{
				this.AddWarningLog("Стратегия в состоянии {0}. Перерегистрация заявки невозможна.", ProcessState);
				return;
			}

			if (oldOrder.Type == OrderTypes.Conditional)
				_pendingStopOrders.Add(newOrder);
			else
				_pendingOrders.Add(newOrder);

			_ordersInfo.Add(newOrder, new OrderInfo { IsOwn = true });

			//if (newOrder.Strategy == null)
			//	newOrder.Strategy = this;

			ApplyMonitorRules(newOrder);

			ReRegisterSlippage(oldOrder, newOrder);

			if (oldOrder.Type == OrderTypes.Conditional)
				OnStopOrderReRegistering(oldOrder, newOrder);
			else
				OnOrderReRegistering(oldOrder, newOrder);

			Trader.ReRegisterOrder(oldOrder, newOrder);
		}

		private void ApplyMonitorRules(Order order)
		{
			if (CancelOrdersWhenStopping)
			{
				IMarketRule matchedRule = order.WhenMatched();

				if (WaitAllTrades)
					matchedRule = matchedRule.And(order.WhenAllTrades());

				order
					.WhenCanceled()
					.Or(matchedRule, order.WhenRegisterFailed())
					.Do(() => this.AddInfoLog("Заявка {0} больше не активна.".Put(order.TransactionId)))
					.Until(() =>
					{
						if (order.State == OrderStates.Failed)
							return true;

						using (order.BeginRead())
						{
							if (order.State != OrderStates.Done)
								return false;

							if (!WaitAllTrades)
								return true;

							var matchedVolume = order.GetMatchedVolume();

							if (matchedVolume == 0)
								return true;

							var info = _ordersInfo.TryGetValue(order);

							if (info == null)
								return false;

							return matchedVolume == info.ReceivedVolume;
						}
					})
					.Apply(this);
			}
		}

		/// <summary>
		/// Отменить заявку.
		/// </summary>
		/// <param name="order">Заявка для отмены.</param>
		public virtual void CancelOrder(Order order)
		{
			if (ProcessState != ProcessStates.Started)
			{
				this.AddWarningLog("Стратегия в состоянии {0}. Отмена заявки невозможна.", ProcessState);
				return;
			}

			if (order == null)
				throw new ArgumentNullException("order");

			lock (_ordersInfo.SyncRoot)
			{
				var info = _ordersInfo.TryGetValue(order);

				if (info == null || !info.IsOwn)
					throw new ArgumentException("Заявка {0} не принадлежит стратегии {1}.".Put(order.TransactionId, Name));

				if (info.IsCanceled)
				{
					this.AddWarningLog("Для заявки {0} уже был послан сигнал на отмену.", order.TransactionId);
					return;
				}

				info.IsCanceled = true;
			}

			ThrowIfTraderNotRegistered();
			CancelOrderHandler(order);
		}

		private void CancelOrderHandler(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			this.AddInfoLog("Отмена заявки {0}.", order.TransactionId);

			if (order.Type == OrderTypes.Conditional)
				OnStopOrderCanceling(order);
			else
				OnOrderCanceling(order);

			Trader.CancelOrder(order);
		}

		private event Action<Order, Order> ReRegistering;

		/// <summary>
		/// Уведомить <see cref="SlippageManager"/> о перерегистрации заявки.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять и на основе нее зарегистрировать новую.</param>
		/// <param name="newOrder">Новая заявка.</param>
		protected void ReRegisterSlippage(Order oldOrder, Order newOrder)
		{
			this.AddInfoLog("Перерегистрация проскальзывания заявки {0} (0x{1:X}) на заявку (0x{2:X}).", oldOrder.TransactionId, oldOrder.GetHashCode(), newOrder.GetHashCode());

			SlippageManager.ReRegistering(oldOrder, newOrder);
			ReRegistering.SafeInvoke(oldOrder, newOrder);
		}

		/// <summary>
		/// Добавить заявку в стратегию.
		/// </summary>
		/// <param name="order">Заявка.</param>
		public void AddOrder(Order order)
		{
			if (order == null)
				throw new ArgumentNullException("order");

			lock (_ordersInfo.SyncRoot)
			{
				var info = _ordersInfo.SafeAdd(order);

				if (info.IsHandled)
					return;

				info.IsHandled = true;
			}

			TryInvoke(() =>
			{
				ThrowIfTraderNotRegistered();

				var isLatChanged = LatencyManager.ProcessNewOrder(order) != TimeSpan.Zero;

				if (order.Type == OrderTypes.Conditional)
				{
					_stopOrders.Add(order);
					OnStopOrderRegistered(order);

					StatisticManager.AddNewOrder(order);
				}
				else
				{
					_orders.Add(order);
					OnOrderRegistered(order);

					//SlippageManager.Registered(order);

					var isComChanged = CommissionManager.ProcessOrder(order) != 0;
					var isPosChanged = PositionManager.ProcessOrder(order) != 0;

					StatisticManager.AddNewOrder(order);

					if (isComChanged)
						RaiseCommissionChanged();

					if (isPosChanged)
						RaisePositionChanged();
				}

				if (_firstOrderTime == default(DateTime))
					_firstOrderTime = order.Time;

				_lastOrderTime = order.Time;

				RecycleOrders();

				if (isLatChanged)
					RaiseLatencyChanged();

				if (ProcessState == ProcessStates.Stopping && CancelOrdersWhenStopping)
				{
					lock (_ordersInfo.SyncRoot)
					{
						var info = _ordersInfo.TryGetValue(order);

						// заявка принадлежит дочерней стратегии
						if (info == null || !info.IsOwn)
							return;

						// для заявки уже был послан сигнал на снятие
						if (info.IsCanceled)
							return;

						info.IsCanceled = true;
					}

					CancelOrderHandler(order);
				}
			});
		}

		/// <summary>
		/// Добавить активную заявку в стратегию и обработать сделки по заявке.
		/// </summary>
		/// <remarks>
		/// Используется для восстановления состояния стратегии, когда необходимо
		/// подписаться на получение данных по заявкам, зарегистрированным ранее.
		/// </remarks>
		/// <param name="order">Заявка.</param>
		/// <param name="myTrades">Сделки по заявке.</param>
		public void AttachOrder(Order order, IEnumerable<MyTrade> myTrades)
		{
			if(order == null)
				throw new ArgumentNullException("order");

			if (myTrades == null)
				throw new ArgumentNullException("myTrades");

			_ordersInfo.Add(order, new OrderInfo { IsOwn = true });
			ApplyMonitorRules(order);

			PositionManager.ProcessOrder(order);

			if(order.Type == OrderTypes.Conditional)
			{
				_pendingStopOrders.Add(order);
				ProcessNewOrders(Trader.StopOrders, true).ForEach(AddOrder);
			}
			else
			{
				_pendingOrders.Add(order);
				ProcessNewOrders(Trader.Orders, false).ForEach(AddOrder);
			}

			OrderRegistering.SafeInvoke(order);

			OnTraderNewMyTrades(myTrades);
		}

		private void RecycleOrders()
		{
			if (OrdersKeepTime == TimeSpan.Zero)
				return;

			var diff = _lastOrderTime - _firstOrderTime;

			if (diff <= _maxOrdersKeepTime)
				return;

			_firstOrderTime = _lastOrderTime - OrdersKeepTime;

			_orders.SyncDo(d => d.RemoveWhere(o => o.State == OrderStates.Done && o.Time < _firstOrderTime));
			_stopOrders.SyncDo(d => d.RemoveWhere(o => o.State == OrderStates.Done && o.Time < _firstOrderTime));
			_ordersInfo.SyncDo(d => d.RemoveWhere(o => o.Key.State == OrderStates.Done && o.Key.Time < _firstOrderTime));
		}

		private void ChangeOrders(IEnumerable<Order> orders)
		{
			if (orders == null)
				throw new ArgumentNullException("orders");

			TryInvoke(() =>
			{
				foreach (var order in orders)
				{
					ChangeOrder(order);
				}
			});
		}

		private void ChangeOrder(Order order)
		{
			OnOrderChanged(order);

			if (PositionManager.ProcessOrder(order) != 0)
				RaisePositionChanged();

			StatisticManager.AddChangedOrder(order);
		}

		/// <summary>
		/// Текущее время, которое будет передано в <see cref="LogMessage.Time"/>.
		/// </summary>
		public override DateTime CurrentTime
		{
			get
			{
				return this.GetMarketTime();
			}
		}

		/// <summary>
		/// Вызвать событие <see cref="ILogSource.Log"/>.
		/// </summary>
		/// <param name="message">Отладочное сообщение.</param>
		protected override void RaiseLog(LogMessage message)
		{
			if (message == null)
				throw new ArgumentNullException("message");

			switch (message.Level)
			{
				case LogLevels.Warning:
					if (ErrorState == LogLevels.Info)
						ErrorState = LogLevels.Warning;
					break;
				case LogLevels.Error:
					ErrorState = LogLevels.Error;
					break;
			}

			// mika
			// так как некоторые стратегии слишком много пишут в лог, то получается слишком медленно
			//
			//TryInvoke(() => base.RaiseLog(message));
			base.RaiseLog(message);
		}

		/// <summary>
		/// Запустить торговый алгоритм.
		/// </summary>
		public virtual void Start()
		{
			if (ProcessState == ProcessStates.Stopped)
				ProcessState = ProcessStates.Started;
			else 
				this.AddDebugLog("Попытка запустить стратегию в состоянии {0}.", ProcessState);
		}

		/// <summary>
		/// Остановить торговый алгоритм.
		/// </summary>
		public virtual void Stop()
		{
			if (ProcessState == ProcessStates.Started)
				ProcessState = ProcessStates.Stopping;
			else
				this.AddDebugLog("Попытка остановить стратегию в состоянии {0}.", ProcessState);
		}

		/// <summary>
		/// Событие переинициализации стратегии.
		/// </summary>
		public event Action Reseted;

		/// <summary>
		/// Вызвать событие <see cref="Reseted"/>.
		/// </summary>
		protected void RaiseReseted()
		{
			Reseted.SafeInvoke();
		}

		/// <summary>
		/// Переинициализировать торговый алгоритм.
		/// Вызывается после инициализации объекта стратегии и загрузки сохраненных параметров.
		/// </summary>
		public virtual void Reset()
		{
			this.AddInfoLog("Переинициализация.");
			
			ThrowIfTraderNotRegistered();

			if (Security == null)
				throw new InvalidOperationException("Инструмент не инициализирован.");

			if (Portfolio == null)
				throw new InvalidOperationException("Портфель не инициализирован.");

			ChildStrategies.SyncDo(c => c.ForEach(s => s.Reset()));

			StatisticManager.Reset();
			PnLManager.Reset();
			CommissionManager.Reset();
			LatencyManager.Reset();
			PositionManager.Reset();
			SlippageManager.Reset();

			_orders.Clear();
			_orderFails.Clear();
			_stopOrders.Clear();
			_myTrades.Clear();

			_pendingOrders.Clear();
			_pendingStopOrders.Clear();
			_ordersInfo.Clear();

			ProcessState = ProcessStates.Stopped;
			ErrorState = LogLevels.Info;
			ErrorCount = 0;

			_firstOrderTime = _lastOrderTime = default(DateTime);
			OnReseted();
		}

		/// <summary>
		/// Вызывается из метода <see cref="Reset"/>. 
		/// </summary>
		protected virtual void OnReseted()
		{
			RaiseReseted();
		}

		/// <summary>
		/// Приостановить исполнение правил до следующего восстановления через метод <see cref="ResumeRules"/>.
		/// </summary>
		public virtual void SuspendRules()
		{
			lock (_rulesSuspendLock)
				_rulesSuspendCount++;

			this.AddInfoLog("Приостановка правил. _rulesSuspendCount {0}.", _rulesSuspendCount);
		}

		/// <summary>
		/// Восстановить исполнение правил, остановленное через метод <see cref="SuspendRules()"/>.
		/// </summary>
		public virtual void ResumeRules()
		{
			lock (_rulesSuspendLock)
			{
				if (_rulesSuspendCount > 0)
					_rulesSuspendCount--;
			}

			this.AddInfoLog("Возобновление правил. _rulesSuspendCount {0}.", _rulesSuspendCount);
		}

		private void TryFinalStop()
		{
			if (!Rules.IsEmpty())
			{
				this.AddLog(LogLevels.Debug,
					() => "Попытка остановки. Осталось {0} правил. Правила {1}."
						.Put(Rules.Count, Rules.SyncGet(c => c.Select(r => r.Name).Join(", "))));

				return;
			}

			ProcessState = ProcessStates.Stopped;

			if (DisposeOnStop)
			{
				//Trace.WriteLine(Name+" strategy-dispose-on-stop");

				if (Parent != null)
					Parent.ChildStrategies.Remove(this);

				Dispose();
			}
		}

		void IMarketRuleContainer.ActivateRule(IMarketRule rule, Func<bool> process)
		{
			//if (rule == null)
			//	throw new ArgumentNullException("rule");

			//if (process == null)
			//	throw new ArgumentNullException("process");

			if (_rulesSuspendCount > 0)
			{
				this.AddRuleLog(LogLevels.Debug, rule, "Не может быть обработано, так как приостановлено исполнение правил.");
				return;
			}

			try
			{
				this.ActiveRule(rule, process);
			}
			catch (Exception error)
			{
				OnError(error);
			}
			finally
			{
				if (_processState == ProcessStates.Stopping)
					TryFinalStop();
			}
		}

		/// <summary>
		/// Интервал обработки события <see cref="SecurityChanged"/> для изменения <see cref="PnL"/>.
		/// </summary>
		/// <remarks>
		/// Значение по-умолчанию равно 1 минуте.
		/// </remarks>
		[Browsable(false)]
		public TimeSpan UnrealizedPnLInterval { get; set; }

		private DateTime _nextProcessTime;

		/// <summary>
		/// Метод, который вызывается при изменении инструмента стратегии.
		/// </summary>
		protected virtual void OnSecurityChanged()
		{
			SecurityChanged.SafeInvoke();

			if (Trader == null)
				return;

			var marketTime = this.GetMarketTime();

			if (_nextProcessTime > marketTime + UnrealizedPnLInterval)
				_nextProcessTime = marketTime + UnrealizedPnLInterval;

			if (marketTime < _nextProcessTime)
				return;

			_nextProcessTime = marketTime + UnrealizedPnLInterval;

			if (Position != 0)
				RaisePnLChanged();
		}

		/// <summary>
		/// Метод, который вызывается при изменении портфеля стратегии.
		/// </summary>
		protected virtual void OnPortfolioChanged()
		{
			PortfolioChanged.SafeInvoke();
		}

		/// <summary>
		/// Метод, который вызывается при изменении позиций стратегии.
		/// </summary>
		/// <param name="positions">Измененные позиции стратегии.</param>
		protected virtual void OnPositionsChanged(IEnumerable<Position> positions)
		{
			PositionsChanged.SafeInvoke(positions);
		}

		/// <summary>
		/// Метод, который вызывается при появлении новых сделок стратегии.
		/// </summary>
		/// <param name="trades">Новые сделки стратегии.</param>
		protected virtual void OnNewMyTrades(IEnumerable<MyTrade> trades)
		{
			NewMyTrades.SafeInvoke(trades);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrderRegistering"/>.
		/// </summary>
		/// <param name="order">Заявка.</param>
		protected virtual void OnOrderRegistering(Order order)
		{
			SlippageManager.Registering(order);
			OrderRegistering.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrderRegistered"/>.
		/// </summary>
		/// <param name="order">Заявка.</param>
		protected virtual void OnOrderRegistered(Order order)
		{
			OrderRegistered.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrderRegistering"/>.
		/// </summary>
		/// <param name="order">Стоп-заявка.</param>
		protected virtual void OnStopOrderRegistering(Order order)
		{
			SlippageManager.Registering(order);
			StopOrderRegistering.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrderRegistered"/>.
		/// </summary>
		/// <param name="order">Стоп-заявка.</param>
		protected virtual void OnStopOrderRegistered(Order order)
		{
			StopOrderRegistered.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrderRegistered"/>.
		/// </summary>
		/// <param name="order">Стоп-заявка.</param>
		protected virtual void OnStopOrderCanceling(Order order)
		{
			StopOrderCanceling.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrderRegistered"/>.
		/// </summary>
		/// <param name="order">Заявка.</param>
		protected virtual void OnOrderCanceling(Order order)
		{
			OrderCanceling.SafeInvoke(order);
		}

		/// <summary>
		/// Вызвать событие <see cref="StopOrderReRegistering"/>.
		/// </summary>
		/// <param name="oldOrder">Стоп-заявка, которую нужно снять.</param>
		/// <param name="newOrder">Новая стоп-заявка, которую нужно зарегистрировать.</param>
		protected virtual void OnStopOrderReRegistering(Order oldOrder, Order newOrder)
		{
			StopOrderReRegistering.SafeInvoke(oldOrder, newOrder);
		}

		/// <summary>
		/// Вызвать событие <see cref="OrderReRegistering"/>.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять.</param>
		/// <param name="newOrder">Новая заявка, которую нужно зарегистрировать.</param>
		protected virtual void OnOrderReRegistering(Order oldOrder, Order newOrder)
		{
			OrderReRegistering.SafeInvoke(oldOrder, newOrder);
		}

		/// <summary>
		/// Метод, который вызывается при изменении заявки стратегии.
		/// </summary>
		/// <param name="order">Измененная заявка.</param>
		protected virtual void OnOrderChanged(Order order)
		{
			OrderChanged.SafeInvoke(order);

			if (LatencyManager.ProcessChangedOrder(order) != TimeSpan.Zero)
				RaiseLatencyChanged();
		}
	
		/// <summary>
		/// Метод, который вызывается при изменении стоп-заявки стратегии.
		/// </summary>
		/// <param name="order">Измененная стоп-заявка.</param>
		protected virtual void OnStopOrderChanged(Order order)
		{
			StopOrderChanged.SafeInvoke(order);
			StatisticManager.AddChangedOrder(order);
		}

		/// <summary>
		/// Метод, который вызывается при изменении стоп-заявок стратегии.
		/// </summary>
		/// <param name="orders">Измененные стоп-заявки.</param>
		protected virtual void OnStopOrdersChanged(IEnumerable<Order> orders)
		{
			foreach (var order in orders)
			{
				OnStopOrderChanged(order);

				if (order.DerivedOrder == null)
					continue;

				lock (_ordersInfo.SyncRoot)
				{
					var derivedOrder = order.DerivedOrder;

					if (_ordersInfo.ContainsKey(derivedOrder))
						continue;

					_ordersInfo.Add(derivedOrder, new OrderInfo { IsOwn = true });
					AddOrder(derivedOrder);

					//заявка могла придти позже сделок по ней
					OnTraderNewMyTrades(Trader.MyTrades.Where(t => t.Order == derivedOrder));
				}
			}
		}

		/// <summary>
		/// Метод, который вызывается при ошибке регистрации заявки стратегии.
		/// </summary>
		/// <param name="fail">Ошибка регистрации заявки.</param>
		protected virtual void OnOrderFailed(OrderFail fail)
		{
			OrderRegisterFailed.SafeInvoke(fail);
			StatisticManager.AddRegisterFailedOrder(fail);
		}

		/// <summary>
		/// Метод, который вызывается при ошибке регистрации стоп-заявки стратегии.
		/// </summary>
		/// <param name="fail">Ошибка регистрации стоп-заявки.</param>
		protected virtual void OnStopOrderFailed(OrderFail fail)
		{
			StopOrderRegisterFailed.SafeInvoke(fail);
			StatisticManager.AddRegisterFailedOrder(fail);
		}

		/// <summary>
		/// Метод, который вызывается при ошибке отмены заявки стратегии.
		/// </summary>
		/// <param name="fail">Ошибка отмены заявки.</param>
		protected virtual void OnOrderCancelFailed(OrderFail fail)
		{
			OrderCancelFailed.SafeInvoke(fail);
		}

		/// <summary>
		/// Метод, который вызывается при ошибке отмены стоп-заявки стратегии.
		/// </summary>
		/// <param name="fail">Ошибка отмены стоп-заявки.</param>
		protected virtual void OnStopOrderCancelFailed(OrderFail fail)
		{
			StopOrderCancelFailed.SafeInvoke(fail);
		}

		private void OnTraderSecuritiesChanged(IEnumerable<Security> securities)
		{
			if (securities.Contains(Security))
				TryInvoke(OnSecurityChanged);
		}

		private void OnTraderPortfoliosChanged(IEnumerable<Portfolio> portfolios)
		{
			if (portfolios.Contains(Portfolio))
				TryInvoke(OnPortfolioChanged);
		}

		private void OnTraderPositionsChanged(IEnumerable<Position> positions)
		{
			Position[] changedPositions;

			if (Portfolio is BasketPortfolio)
			{
				var innerPfs = ((BasketPortfolio)Portfolio).InnerPortfolios;
				changedPositions = positions.Where(pos => innerPfs.Contains(pos.Portfolio)).ToArray();
			}
			else
			{
				changedPositions = positions.Where(p => p.Portfolio == Portfolio).ToArray();
			}

			if (changedPositions.Length > 0)
				TryInvoke(() => OnPositionsChanged(changedPositions));
		}

		private void OnTraderNewMyTrades(IEnumerable<MyTrade> trades)
		{
			trades = trades.Where(t => IsOwnOrder(t.Order)).ToArray();

			if (trades.IsEmpty())
				return;
			
			AddMyTrades(trades);
		}

		private void OnTraderNewOrders(IEnumerable<Order> orders)
		{
			lock (_orders.SyncRoot)
				ProcessNewOrders(orders, false).ForEach(AddOrder);
		}

		private void OnTraderNewStopOrders(IEnumerable<Order> orders)
		{
			lock (_stopOrders.SyncRoot)
				ProcessNewOrders(orders, true).ForEach(AddOrder);
		}

		private void OnTraderOrdersChanged(IEnumerable<Order> orders)
		{
			orders = orders.Where(IsOwnOrder).ToArray();

			if (orders.IsEmpty())
				return;

			ChangeOrders(orders);
		}

		private void OnTraderStopOrdersChanged(IEnumerable<Order> orders)
		{
			orders = orders.Where(_stopOrders.Contains).ToArray();

			if (!orders.IsEmpty())
				TryInvoke(() => OnStopOrdersChanged(orders));
		}

		private void OnTraderOrdersRegisterFailed(IEnumerable<OrderFail> fails)
		{
			ProcessRegisterOrderFails(_pendingOrders, fails, OnOrderFailed);
		}

		private void OnTraderStopOrdersRegisterFailed(IEnumerable<OrderFail> fails)
		{
			ProcessRegisterOrderFails(_pendingStopOrders, fails, OnStopOrderFailed);
		}

		private void OnTraderOrdersCancelFailed(IEnumerable<OrderFail> fails)
		{
			ProcessCancelOrderFails(fails);
		}

		private void OnTraderStopOrdersCancelFailed(IEnumerable<OrderFail> fails)
		{
			ProcessCancelOrderFails(fails);
		}

		private void AddMyTrades(IEnumerable<MyTrade> trades)
		{
			var filteredTrades = _myTrades.SyncGet(set =>
			{
				var newTrades = trades.Where(t => !set.Contains(t)).ToArray();
				set.AddRange(newTrades);
				return newTrades;
			});

			if (filteredTrades.IsEmpty())
				return;

			if (WaitAllTrades)
			{
				foreach (var trade in filteredTrades)
				{
					lock (_ordersInfo.SyncRoot)
					{
						var info = _ordersInfo.TryGetValue(trade.Order);
						if (info == null || !info.IsOwn)
							continue;

						info.ReceivedVolume += trade.Trade.Volume;
					}
				}
			}

			TryInvoke(() => OnNewMyTrades(filteredTrades));

			var isComChanged = false;
			var isPnLChanged = false;
			var isPosChanged = false;
			var isSlipChanged = false;

			foreach (var trade in filteredTrades)
			{
				this.AddInfoLog("Новая {0} сделка {1} по цене {2} на {3} заявки {4}.",
								trade.Order.Direction, trade.Trade.Id, trade.Trade.Price,
								trade.Trade.Volume, trade.Order.TransactionId);

				if (CommissionManager.ProcessMyTrade(trade) != 0)
					isComChanged = true;

				var tradeInfo = PnLManager.ProcessMyTrade(trade);
				if (tradeInfo.PnL != 0)
					isPnLChanged = true;

				if (PositionManager.ProcessMyTrade(trade) != 0)
					isPosChanged = true;

				if (SlippageManager.AddTrade(trade) != 0)
					isSlipChanged = true;

				StatisticManager.AddMyTrade(tradeInfo);
			}

			TryInvoke(() =>
			{
				if (isComChanged)
					RaiseCommissionChanged();

				if (isPnLChanged)
					RaisePnLChanged();

				if (isPosChanged)
					RaisePositionChanged();

				if (isSlipChanged)
					RaiseSlippageChanged();
			});
		}

		private void RaiseSlippageChanged()
		{
			this.Notify("Slippage");
			SlippageChanged.SafeInvoke();
		}

		private void RaisePositionChanged()
		{
			this.AddInfoLog("Новая позиция: {0}.", PositionManager.Positions.Select(pos => pos + "=" + pos.CurrentValue).Join(", "));

			this.Notify("Position");
			PositionChanged.SafeInvoke();

			StatisticManager.AddPosition(this.GetMarketTime(), Position);
		}

		private void RaiseCommissionChanged()
		{
			this.Notify("Commission");
			CommissionChanged.SafeInvoke();
		}

		private void RaisePnLChanged()
		{
			this.Notify("PnL");
			PnLChanged.SafeInvoke();

			StatisticManager.AddPnL(this.GetMarketTime(), PnL);
		}

		private void RaiseLatencyChanged()
		{
			this.Notify("Latency");
			LatencyChanged.SafeInvoke();
		}

		/// <summary>
		/// Обработать поступившие от шлюза <see cref="Trader"/> заявки, и найти из них те, что принадлежат стратегии.
		/// </summary>
		/// <param name="newOrders">Новые заявки.</param>
		/// <param name="isStopOrders">Признак того, пришли ли это обычные заявки или стоп-заявки.</param>
		/// <returns>Заявки, принадлежащие стратегии.</returns>
		protected virtual IEnumerable<Order> ProcessNewOrders(IEnumerable<Order> newOrders, bool isStopOrders)
		{
			return (isStopOrders ? _pendingStopOrders : _pendingOrders).SyncGet(c =>
			{
				var retVal = newOrders.Where(c.Contains).ToArray();
				c.RemoveRange(retVal);
				return retVal;
			});
		}

		/// <summary>
		/// Заявки, ожидающие отправленные на регистрацию, но еще не получившие ответа.
		/// </summary>
		[Browsable(false)]
		public IEnumerable<Order> PendingOrders
		{
			get { return _pendingOrders.Cache; }
		}

		/// <summary>
		/// Загрузить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public virtual void Load(SettingsStorage storage)
		{
			var parameters = storage.GetValue<SettingsStorage>("Parameters");

			if (parameters == null)
				return;

			var dict = Parameters.SyncGet(c => c.ToDictionary(p => p.Name, p => p, StringComparer.InvariantCultureIgnoreCase));

			foreach (var pair in parameters)
				dict[pair.Key].Value = pair.Value;
		}

		/// <summary>
		/// Сохранить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public virtual void Save(SettingsStorage storage)
		{
			var paramStorage = new SettingsStorage();

			foreach (var strategyParam in Parameters.SyncGet(c => c.ToArray()))
				paramStorage.SetValue(strategyParam.Name, strategyParam.Value);

			storage.SetValue("Parameters", paramStorage);
		}

		/// <summary>
		/// Событие изменения параметров стратегии.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		void INotifyPropertyChangedEx.NotifyPropertyChanged(string info)
		{
			PropertyChanged.SafeInvoke(this, info);
		}

		/// <summary>
		/// Отменить все активные заявки (стоп и обычные).
		/// </summary>
		public void CancelActiveOrders()
		{
			if (ProcessState != ProcessStates.Started)
			{
				this.AddWarningLog("Стратегия в состоянии {0}. Отмена заявок невозможна.", ProcessState);
				return;
			}

			this.AddInfoLog("Снятие всех активных заявок.");

			ProcessCancelActiveOrders();
		}

		private void ProcessCancelActiveOrders()
		{
			ThrowIfTraderNotRegistered();

			_ordersInfo.SyncGet(d => d.Keys.Filter(OrderStates.Active).ToArray()).ForEach(o =>
			{
				var info = _ordersInfo.TryGetValue(o);

				//заявка принадлежит дочерней статегии
				if (!info.IsOwn)
					return;

				if (info.IsCanceled)
				{
					this.AddWarningLog("Для заявки {0} уже был послан сигнал на отмену.", o.TransactionId);
					return;
				}

				info.IsCanceled = true;

				CancelOrderHandler(o);
			});
		}

		private void OnChildOrderChanged(Order order)
		{
			ChangeOrder(order);
		}

		private void OnChildStopOrderChanged(Order order)
		{
			TryInvoke(() => StopOrderChanged.SafeInvoke(order));
		}

		private void OnChildOrderRegisterFailed(OrderFail fail)
		{
			SlippageManager.RegisterFailed(fail);
			TryInvoke(() => OrderRegisterFailed.SafeInvoke(fail));
		}

		private void OnChildStopOrderRegisterFailed(OrderFail fail)
		{
			SlippageManager.RegisterFailed(fail);
			TryInvoke(() => StopOrderRegisterFailed.SafeInvoke(fail));
		}

		private void OnChildOrderCancelFailed(OrderFail fail)
		{
			TryInvoke(() => OrderCancelFailed.SafeInvoke(fail));
		}

		private void OnChildStopOrderCancelFailed(OrderFail fail)
		{
			TryInvoke(() => StopOrderCancelFailed.SafeInvoke(fail));
		}

		private void ProcessCancelOrderFails(IEnumerable<OrderFail> fails)
		{
			foreach (var fail in fails)
			{
				var order = fail.Order;

				lock (_ordersInfo.SyncRoot)
				{
					var info = _ordersInfo.TryGetValue(order);

					if (info == null || !info.IsOwn)
						continue;

					info.IsCanceled = false;
				}

				this.AddErrorLog("Заявка {0} не была отменена по причине {1}.", order.TransactionId, fail.Error);

				if (order.Type == OrderTypes.Conditional)
					OnStopOrderCancelFailed(fail);
				else
					OnOrderCancelFailed(fail);

				StatisticManager.AddFailedOrderCancel(fail);
			}
		}

		private void ProcessRegisterOrderFails(SynchronizedSet<Order> order, IEnumerable<OrderFail> fails, Action<OrderFail> evt)
		{
			var failedOrders = order.SyncGet(c =>
			{
				var retVal = new List<OrderFail>();

				foreach (var fail in fails)
				{
					if (c.Contains(fail.Order))
					{
						retVal.Add(fail);
						c.Remove(fail.Order);
					}
				}

				return retVal;
			});

			foreach (var fail in failedOrders)
			{
				this.AddErrorLog("Заявка {0} (0x{2:X}) не была принята по причине {1}.", fail.Order.TransactionId, fail.Error, fail.Order.GetHashCode());
				SlippageManager.RegisterFailed(fail);
			}

			_orderFails.AddRange(failedOrders);
			TryInvoke(() => failedOrders.ForEach(evt));
		}

		/// <summary>
		/// Обработка ошибки, полученной в результате работы стратегии.
		/// </summary>
		/// <param name="error">Ошибка.</param>
		protected virtual void OnError(Exception error)
		{
			ErrorCount++;
			Error.SafeInvoke(error);

			this.AddErrorLog(error.ToString());

			if (ErrorCount >= MaxErrorCount)
				Stop();
		}

		object ICloneable.Clone()
		{
			return Clone();
		}

		/// <summary>
		/// Создать копию стратегии со всеми настройками.
		/// </summary>
		/// <returns>Копия стратегии.</returns>
		public Strategy Clone()
		{
			var clone = GetType().CreateInstance<Strategy>();
			clone.Trader = Trader;
			clone.Security = Security;
			clone.Portfolio = Portfolio;

			var id = clone.Id;
			clone.Load(this.Save());
			clone.Id = id;

			return clone;
		}

		private void ThrowIfTraderNotRegistered()
		{
			if (Trader == null)
				throw new InvalidOperationException("Шлюз не инициализирован.");
		}

		private void TryInvoke(Action handler)
		{
			_disposeLock.Read(() =>
			{
				if (IsDisposed)
					return;

				handler();
			});
		}

		private bool IsOwnOrder(Order order)
		{
			var info = _ordersInfo.TryGetValue(order);
			return info != null && info.IsOwn;
		}

		/// <summary>
		/// Освободить занятые ресурсы.
		/// </summary>
		protected override void DisposeManaged()
		{
			_disposeLock.WriteAsync(() =>
			{
				ChildStrategies.SyncDo(c =>
				{
					c.ForEach(s => s.Dispose());
					c.Clear();
				});

				Trader = null;
			});

			base.DisposeManaged();

			MemoryStatistics.Instance.Strategies.Remove(this);
		}
	}
}
